import React from 'react';
import { useEffect, useMemo, useCallback, useReducer } from 'react';
import {
  Table,
  Pagination,
  Dropdown,
  Button,
  Menu,
  Drawer,
  Tree,
  Checkbox,
  Row,
  Col,
  Popover,
  Space,
  Divider,
  Spin,
  Alert,
  message,
} from 'antd';
import { GetReportInfo, GetReportDataLists, ExportReportDatas } from '@/services/report';
import { useModel, history } from 'umi';
import _ from 'lodash';
import {
  SearchOutlined,
  DownOutlined,
  MinusSquareOutlined,
  PlusSquareOutlined,
} from '@ant-design/icons';
import FilterItem from '../base/FilterItem';
import DataEdit from '../base/DataEdit';
import type { SorterResult } from 'antd/lib/table/interface';
import { renderNumber, getState, setState, getParamVals } from '@/utils/common';
import { format } from '@/utils/datetime';
import SubReport from './subReport';
import LineChart from './lineChart';
import ColumnChart from './columnChart';
import PieChart from './pieChart';
import PointChart from './pieChart';
import LineColumnChart from './lineColumnChart';
import { Base64 } from 'js-base64';
// 属性类型
type PropField = {
  /** 报表配置文件的名称 */
  reportName: string;
  /** 宽度，一般按照报表配置文件中columns的宽度自动生成 */
  width?: number;
  /** 高度，一般按照窗口的高度自动生成 */
  height?: number;
  /** 表格是否有边框，默认:true */
  bordered?: boolean;
  /** 表格尺寸,默认:middle*/
  size?: 'default' | 'middle' | 'small';
  /** 每页显示的数据条数 */
  pageSize?: number;
  /** 是否可以分页 */
  canPage?: boolean;
  /** 是否可以选择每页显示条数 */
  canPageSize?: boolean;
  /** 是否可以选择显示的项目 */
  canSelectItems?: boolean;
  /** 是否显示清除过滤条件按钮 */
  canClearFilter?: boolean;
  /** 是否可以导出 */
  canExport?: boolean;
  /** 是否可以多选 */
  multiSelect?: boolean;
  /** 报表参数值 */
  paramVals?: Record<string, any>;
  /** 查询条件 */
  seeks?: API.WhereCause[];
  /** 初始排序 */
  initSorter?: API.OrderItem;
  /** 额外的按钮 */
  extraButtons?: JSX.Element;
  /** 单位 */
  unit?: number;
  /** 附件的where条件 */
  whereAttach?: string;
  /** 分组层级 */
  groupByIndex?: number;
  /** 是否隐藏参数工具条 */
  hideParamBar?: boolean;
  /** 表格action引用，可以用于表格的刷新,排序 */
  actionRef?: React.MutableRefObject<API.TableAction | undefined>;
  /** 是否刷新报表 */
  refresh?: boolean;
  /** 选择改变时的回调函数 */
  onSelectChange?: (selKeys: React.Key[], selRows: object[]) => Promise<void>;
};
type StateField = {
  reptInfo?: API.ReptInfo;
  listData?: API.ReportDataList;
  columns?: API.ReportColumn[];
  filterControls?: API.Control[];
  width?: number;
  columnDeep?: number;
  curPage?: number;
  pageSize?: number;
  orderBy?: Record<string, string>;
  filters?: API.WhereCause[];
  selKeys?: React.Key[];
  visibleFilterName?: string;
  visibleItemDrawer?: boolean;
  colsDisplay?: Record<string, boolean>;
  calcColVals?: Record<string, string[]>;
  curColName?: string;
  rowIndex?: number;
  paramVals?: Record<string, any>;
  paramControls?: API.Control[];
  groupByIndex?: number;
  canSelectCell?: boolean;
  detailMenuItems?: JSX.Element[];
  subWhereAtt?: string;
  drillDownIndex?: number;
  visibleSubRept?: boolean;
  visibleFilterDrawer?: boolean;
  loading?: boolean;
};
const ReportTable: React.FC<PropField> = (props) => {
  const { bodyHeight, curPermID } = useModel('sysInfo');
  let initialState = {
    reptInfo: undefined,
    listData: undefined,
    columns: [],
    filterControls: [],
    width: 0,
    columnDeep: 0,
    curPage: 1,
    pageSize: props.pageSize,
    orderBy: undefined,
    filters: [],
    selKeys: [],
    visibleFilterName: '',
    visibleItemDrawer: false,
    colsDisplay: {},
    calcColVals: {},
    curColName: '',
    rowIndex: -1,
    paramVals: props.paramVals,
    paramControls: [],
    groupByIndex: props.groupByIndex,
    canSelectCell: false,
    detailMenuItems: [],
    subWhereAtt: '',
    drillDownIndex: -1,
    visibleSubRept: false,
    visibleFilterDrawer: false,
    loading: false,
  };
  const localState = getState(history.location.pathname, props.reportName);
  if (localState !== null) {
    initialState = { ...initialState, ...localState };
  }
  if (!initialState.paramVals || !initialState.paramVals.sdate || !initialState.paramVals.edate) {
    const pvals = getParamVals().paramVals;
    if (pvals && pvals.sdate && pvals.edate) {
      initialState.paramVals = { ...initialState.paramVals, ...pvals };
    }
  }
  const [state, dispatch] = useReducer((prevState: StateField, newState: StateField) => {
    return { ...prevState, ...newState };
  }, initialState);
  //获取报表信息
  useEffect(() => {
    const paramVals = props.refresh ? props.paramVals! : state.paramVals!;
    Object.keys(paramVals).forEach((key) => {
      if (paramVals[key] instanceof Date) {
        paramVals[key] = format(paramVals[key], 'yyyy-MM-dd');
      } else if (typeof paramVals[key] === 'number') {
        paramVals[key] = '' + paramVals[key];
      }
    });
    const repReq: API.ReportRequest = {
      permID: curPermID,
      configName: props.reportName,
      reportParams: paramVals,
      groupByIndex: state.groupByIndex!,
      calcs: state.calcColVals!,
      unit: 0,
      whereAttach: props.whereAttach,
      whereCauses: [],
      orderBY: {},
      offset: 0,
      limit: 0,
      colsDisplay: {},
    };
    dispatch({
      loading: true,
    });
    GetReportInfo(repReq)
      .then(function (resp) {
        if (resp == undefined || resp.code == undefined || resp.code === 1) {
          message.error(`报表请求异常:${resp.message}`);
          dispatch({
            loading: false,
          });
          return;
        }
        const data = resp.data;
        const initColsDisplay = () => {
          const colsDis: Record<string, boolean> = {};
          const loop = (cols: API.ReportColumn[]) =>
            cols.forEach((item) => {
              if (item.children) {
                loop(item.children);
              }
              if (item.dispaly) {
                colsDis[item.key] = true;
              }
            });
          loop(data!.columns!);
          return colsDis;
        };
        const colControls: API.Control[] = [];
        const getColumns = (c: API.ReportColumn) => {
          if (c.children) {
            c.children.map((ch) => {
              getColumns(ch);
            });
          } else {
            let type: 'number' | 'text' | 'date' | 'datetime' = 'text';
            if (c.dataType === 'int' || c.dataType === 'float' || c.dataType === 'number') {
              type = 'number';
            } else if (c.dataType === 'date') {
              type = 'date';
            } else if (c.dataType === 'datetime') {
              type = 'datetime';
            }
            colControls.push({
              name: c.key,
              type,
              label: c.title as string,
              dataType: type === 'text' ? 'string' : type,
            });
          }
        };
        data.columns.forEach((col) => {
          getColumns(col);
        });
        const pConts: API.Control[] = [];
        const pVals: Record<string, any> = {};
        data.params.forEach((p) => {
          pVals[p.name] = p.initVal;
          if (p.type === 'none') {
            return;
          }
          const pCont: API.Control = {
            name: p.name,
            label: p.label,
            type: p.type as 'text' | 'number' | 'select' | 'date' | 'datetime' | 'month' | 'search',
            dataType:
              p.dataType === 'int' || p.dataType === 'float'
                ? 'number'
                : (p.dataType as 'date' | 'datetime' | 'string'),
            multiple: p.multiSelect,
            placeholder: p.placeholder,
            options: p.data,
            fetch: p.fetch,
            grid: p.grid,
            hasAll: p.hasAll,
            precision: 2,
          };
          pConts.push(pCont);
        });
        let selCell = false;
        if (!_.isEmpty(data?.drillDowns)) {
          for (let i = 0; i < data!.drillDowns.length; i++) {
            if (data!.drillDowns[i].refColNames && data!.drillDowns[i].refColNames.length > 0) {
              selCell = true;
              break;
            }
          }
        }
        const closDis = initColsDisplay();
        dispatch({
          reptInfo: data,
          paramVals: pVals,
          colsDisplay: closDis,
          filterControls: colControls,
          paramControls: pConts,
          canSelectCell: selCell,
          loading: false,
          curPage: props.refresh ? 1 : state.curPage,
        });
      })
      .catch(function (ex) {
        console.error('请求报表', props.reportName, '信息错误', ex);
      });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [
    state.calcColVals,
    state.groupByIndex,
    props.reportName,
    props.paramVals,
    props.whereAttach,
    curPermID,
  ]);
  const getChildAllDisplay = useCallback(
    (c: API.ReportColumn) => {
      let canFold = false;
      let ccol = null;
      for (let i = 0; i < c.children.length; i++) {
        ccol = c.children[i];
        if (ccol.children && ccol.title !== '') {
          canFold = true;
          if (i > 0) {
            const cccol = ccol.children[0];
            return { canFold, isFold: state.colsDisplay![cccol.key] };
          }
        } else if (c.children.length > 1 && !ccol.children) {
          canFold = true;
          if (!state.colsDisplay![ccol.key]) {
            return { canFold, isFold: false };
          }
        }
      }
      return { canFold, isFold: true };
    },
    [state.colsDisplay],
  );

  const handleCellClick = useCallback(
    (col: API.ReportColumn, record: Record<string, any>, rowInd: number) => {
      let bgc: string | undefined = undefined;
      if (state.canSelectCell && col.dataIndex === state.curColName && rowInd === state.rowIndex) {
        bgc = '#E6F7FF';
      }
      return {
        onClick: () => {
          const drillDowns = state.reptInfo?.drillDowns;
          if (!drillDowns) {
            return;
          }
          if (col.dataType === 'string') {
            return;
          }
          const menuItems: JSX.Element[] = [];
          let subWA = '';
          drillDowns.map((item, i) => {
            let find = false;
            if (item.refColNames && item.refColNames.length > 0) {
              if (item.refColNames[0] === 'ALL') {
                find = true;
              } else {
                const index = item.refColNames.findIndex(
                  (name) => name === col.dataIndex || name === col.colExp,
                );
                if (index !== -1) {
                  find = true;
                }
              }
            }
            if (find) {
              if (item.refColWheres) {
                subWA = item.refColWheres[col.dataIndex as string];
                if (!state.subWhereAtt) {
                  subWA = item.refColWheres[col.dataIndex];
                  if (!subWA) {
                    subWA = '';
                  }
                }
              }
              if (
                item.reptName == '#Dept1Analysis' &&
                (Number(record.EMID) > 0 || Number(record.DPID) > 0 || Number(record.PDID) > 0)
              ) {
                return;
              } else if (
                item.reptName == '#Dept2Analysis' &&
                (Number(record.EMID) > 0 || Number(record.DPID) > 0)
              ) {
                return;
              } else if (
                item.reptName == '#EmplAnalysis' &&
                Number(record.EMID) == -1 &&
                Number(record.DPID) == -1 &&
                Number(record.PDID) == -1
              ) {
                return;
              }
              menuItems.push(<Menu.Item key={i as React.Key}>{item.title}</Menu.Item>);
            }
          });
          dispatch({
            detailMenuItems: menuItems,
            subWhereAtt: subWA,
            visibleSubRept: false,
            curColName: col.key,
            rowIndex: rowInd,
          });
        },
        style: { background: bgc, cursor: 'pointer' },
      };
    },
    [
      state.rowIndex,
      state.reptInfo?.drillDowns,
      state.subWhereAtt,
      state.canSelectCell,
      state.curColName,
    ],
  );
  const handleColCalcChange = useCallback(
    (colName: string, calcNames: string[]) => {
      const calcs = calcNames;
      const ccvs = { ...state.calcColVals, [colName]: calcs };
      dispatch({ calcColVals: ccvs });
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [],
  );
  const handleDrillDownClick = useCallback(
    (key: string) => {
      const ddIndex = Number(key);
      if (state.rowIndex! < 0) {
        return;
      }
      dispatch({ drillDownIndex: ddIndex, visibleSubRept: true });
    },
    [state.rowIndex],
  );
  const renderCell = useCallback((col: API.ReportColumn, value: string) => {
    let val: string = value;
    if (col.dataType === 'float') {
      val = renderNumber(col.format, value);
    }
    return val;
  }, []);
  //生成报表列
  useEffect(() => {
    if (_.isEmpty(state.reptInfo?.columns)) {
      return;
    }
    const foldChildren = (col: API.ReportColumn, isFold: boolean) => {
      let ccol = null;
      const colsDis = { ...state.colsDisplay };
      let hasZx = false;
      for (let i = 0; i < col.children.length; i++) {
        ccol = col.children[i];
        if (!ccol.children) {
          hasZx = true;
          break;
        }
      }
      for (let i = 0; i < col.children.length; i++) {
        ccol = col.children[i];
        if (ccol.children) {
          for (let j = 0; j < ccol.children.length; j++) {
            const cccol = ccol.children[j];
            if (!cccol.children) {
              if (!hasZx && i === 0) {
                continue;
              }
              if (j === 0) {
                colsDis[cccol.key] = isFold;
              } else {
                colsDis[cccol.key] = false;
              }
            }
          }
        } else {
          if (i === 0) {
            continue;
          }
          colsDis[ccol.key] = isFold;
        }
      }
      dispatch({ colsDisplay: colsDis });
    };
    const cols: API.ReportColumn[] = [];
    let wd: number = 0;
    const getCols = (c: API.ReportColumn) => {
      if (c.children) {
        const children: API.ReportColumn[] = [];
        const ncol = { ...c, children };
        c.children.map((ch) => {
          const cc = getCols(ch);
          if (cc) {
            ncol.children.push(cc);
          }
        });
        if (ncol.children.length > 0) {
          const { canFold, isFold } = getChildAllDisplay(c);
          if (canFold) {
            if (isFold) {
              ncol.title = (
                <div>
                  <MinusSquareOutlined
                    style={{ fontSize: 13 }}
                    onClick={() => foldChildren(c, false)}
                  />{' '}
                  {c.title}
                </div>
              );
            } else {
              ncol.title = (
                <div>
                  <PlusSquareOutlined
                    style={{ fontSize: 13 }}
                    onClick={() => foldChildren(c, true)}
                  />{' '}
                  {c.title}
                </div>
              );
            }
          }
          return ncol;
        }
        return null;
      } else {
        if (state.canSelectCell) {
          c.onCell = (record: object, rowInd: number) => handleCellClick(c, record, rowInd);
          c.render = (value: string) => renderCell(c, value);
        } else {
          if (c.dataType === 'float') {
            c.render = (value: string) => renderNumber(c.format, value);
          }
        }
        if (state.colsDisplay![c.key]) {
          wd += c.width;
          if (c.filter) {
            const cont = state.filterControls!.find((p) => p.name === c.key);
            const filterDatas = state.filters!.find(
              (f) => !_.isEmpty(f.WhereItems) && f.WhereItems[0].FieldName === c.key,
            );
            let visible = false;
            if (c.key === state.visibleFilterName) {
              visible = true;
            }
            c.filterDropdown = (
              <FilterItem
                control={cont!}
                filters={filterDatas}
                unit={c.unit}
                visible={visible}
                onOk={async (filter) => {
                  const idx = state.filters!.findIndex(
                    (f) => f.WhereItems[0].FieldName === filter.WhereItems[0].FieldName,
                  );
                  const newfilters = [...state.filters!];
                  if (idx !== -1) {
                    newfilters[idx] = filter;
                  } else {
                    newfilters.push(filter);
                  }
                  dispatch({ filters: newfilters, curPage: 1 });
                }}
                onCancel={() => dispatch({ visibleFilterName: '' })}
                onClear={(name) => {
                  const idx = state.filters!.findIndex((f) => f.WhereItems[0].FieldName === name);
                  if (idx !== -1) {
                    const newfilters = [...state.filters!];
                    newfilters.splice(idx);
                    dispatch({ filters: newfilters, curPage: 1 });
                  }
                }}
              />
            );

            const idx = state.filters!.findIndex((f) => f.WhereItems[0].FieldName === c.dataIndex);
            c.filterDropdownVisible = visible;
            c.filteredValue = idx !== -1 ? [1] : [];
            c.filterIcon = <SearchOutlined />;
            if (idx !== -1) {
              c.filterIcon = <SearchOutlined style={{ color: '#1890ff' }} />;
            }
            c.onFilterDropdownVisibleChange = () => dispatch({ visibleFilterName: c.dataIndex });
          }
          if (typeof c.title === 'string') {
            const options: JSX.Element[] = [];
            if (!_.isEmpty(state.reptInfo?.calcCols)) {
              state.reptInfo?.calcCols!.map((cc, i) => {
                const ind = cc.refColTypes.findIndex((ct) => ct === c.groupCalc);
                const ind2 = cc.excludeCols ? cc.excludeCols.findIndex((ec) => ec === c.key) : -1;
                if (ind !== -1 && ind2 == -1) {
                  options.push(
                    <Row key={i as React.Key}>
                      <Col span={24}>
                        <Checkbox
                          value={cc.name}
                          onClick={(event) => {
                            event.stopPropagation();
                          }}
                        >
                          {cc.title}
                        </Checkbox>
                      </Col>
                    </Row>,
                  );
                }
              });
            }
            if (options.length > 0) {
              const content = (
                <Checkbox.Group
                  value={state.calcColVals![c.key]}
                  onChange={(calcNames) => {
                    handleColCalcChange(c.key, calcNames as string[]);
                  }}
                >
                  {options}
                </Checkbox.Group>
              );
              c.title = (
                <Popover placement="bottom" title="添加计算列" content={content} trigger="hover">
                  <DownOutlined style={{ fontSize: 10 }} /> {c.title}
                </Popover>
              );
            }
          }
          return c;
        }
        return null;
      }
    };
    const getColumnDeep = (col: API.ReportColumn) => {
      if (_.isEmpty(col.children)) {
        return 1;
      }
      let deep = 0;
      col.children?.forEach((ccol) => {
        const d2 = getColumnDeep(ccol);
        if (d2 > deep) {
          deep = d2;
        }
      });
      return deep + 1;
    };
    state.reptInfo?.columns.map((c) => {
      const cc = getCols(c);
      if (cc) {
        cols.push(cc);
      }
    });
    let maxDeep = 0;
    cols.forEach((c) => {
      const dp = getColumnDeep(c);
      if (dp > maxDeep) {
        maxDeep = dp;
      }
    });
    dispatch({ columns: cols, width: wd, columnDeep: maxDeep });
  }, [
    state.reptInfo?.calcCols,
    state.reptInfo?.columns,
    getChildAllDisplay,
    handleCellClick,
    handleColCalcChange,
    renderCell,
    state.calcColVals,
    state.canSelectCell,
    state.colsDisplay,
    state.filterControls,
    state.filters,
    state.visibleFilterName,
  ]);
  //获取报表数据
  useEffect(() => {
    if (_.isEmpty(state.reptInfo) || _.isEmpty(state.paramVals) || _.isEmpty(state.colsDisplay)) {
      return;
    }
    dispatch({
      loading: true,
    });
    const paramVals = { ...state.paramVals };
    state.reptInfo?.params.forEach((p) => {
      if (p.dataType === 'number') {
        paramVals[p.name] = '' + paramVals[p.name];
      } else if (p.dataType === 'date') {
        paramVals[p.name] = format(paramVals[p.name], 'yyyy-MM-dd');
      }
    });
    const repReq: API.ReportRequest = {
      permID: curPermID,
      configName: props.reportName,
      reportParams: paramVals,
      groupByIndex: state.groupByIndex!,
      calcs: state.calcColVals!,
      unit: 0,
      whereAttach: props.whereAttach!,
      whereCauses: [...state.filters!, ...props.seeks!],
      orderBY: state.orderBy,
      offset: (state.curPage! - 1) * Number(state.pageSize),
      limit: state.pageSize!,
      colsDisplay: state.colsDisplay!,
    };
    GetReportDataLists(repReq)
      .then(function (resp) {
        if (resp.code === 1) {
          dispatch({
            loading: false,
          });
          message.error(`报表请求异常:${resp.message}`);
          return;
        }
        const data = resp.data;
        setState(history.location.pathname, props.reportName, {
          calcColVals: state.calcColVals,
          curPage: state.curPage,
          pageSize: state.pageSize,
          orderBy: state.orderBy,
          colsDisplay: state.colsDisplay,
          filters: state.filters,
          groupByIndex: state.groupByIndex,
          paramVals: state.paramVals,
        });
        dispatch({ listData: data, selKeys: [], loading: false });
      })
      .catch(function (ex) {
        console.error('请求报表', props.reportName, '信息错误', ex);
      });
  }, [
    state.calcColVals,
    props.reportName,
    props.whereAttach,
    curPermID,
    state.curPage,
    state.pageSize,
    state.orderBy,
    state.colsDisplay,
    state.filters,
    props.seeks,
    state.reptInfo,
    state.groupByIndex,
    state.paramVals,
  ]);
  const handleSorterChange = useCallback((order: SorterResult<object>) => {
    if (_.isEmpty(order?.order)) {
      dispatch({ orderBy: undefined });
      return;
    }
    let direction: 'ASC' | 'DESC' = 'ASC';
    if (order.order === 'descend') {
      direction = 'DESC';
    }
    dispatch({ orderBy: { [order.columnKey as string]: direction } });
  }, []);

  const selTreeDrawer = useMemo(() => {
    if (_.isEmpty(state.reptInfo?.columns) || !state.visibleItemDrawer) {
      return null;
    }
    const getDisplayCols = () => {
      const keys: string[] = [];
      Object.keys(state.colsDisplay!).forEach((k) => {
        if (state.colsDisplay![k]) keys.push(k);
      });
      return keys;
    };
    const buildTree = (cols: API.ReportColumn[], pkey: string) =>
      cols.map((item) => {
        if (item.children) {
          const key = pkey + '_' + item.title;
          return (
            <Tree.TreeNode key={key} title={item.title}>
              {buildTree(item.children, key)}
            </Tree.TreeNode>
          );
        }
        if (_.isEmpty(item.title)) {
          return null;
        }
        return <Tree.TreeNode key={item.key} title={item.title} />;
      });
    const selTree = buildTree(state.reptInfo!.columns, '');
    return (
      <Drawer
        title="选择要显示的项目"
        placement="right"
        width={350}
        closable={true}
        onClose={() => dispatch({ visibleItemDrawer: false })}
        visible={state.visibleItemDrawer}
      >
        <Tree
          checkable={true}
          onCheck={(checked) => {
            const names = checked as string[];
            const cols: Record<string, boolean> = {};
            names.forEach((n) => (cols[n] = true));
            dispatch({ colsDisplay: cols });
          }}
          checkedKeys={getDisplayCols()}
        >
          {selTree}
        </Tree>
      </Drawer>
    );
  }, [state.reptInfo, state.visibleItemDrawer, state.colsDisplay]);
  const annoTable = useMemo(() => {
    if (_.isEmpty(state.listData?.annotations)) {
      return null;
    }
    let annoX = 0;
    let annoTotal: number = 0;
    const annoCols: API.ReportColumn[] = [];
    annoTotal = state.listData!.annotations!.total;
    if (annoTotal > 0) {
      state.reptInfo!.annoColumns.map((col) => {
        annoX = annoX + col.width;
        if (col.dispaly) {
          annoCols.push(col);
        }
      });
    }
    return annoTotal > 0 ? (
      <Table
        columns={annoCols}
        dataSource={state.listData?.annotations.datas}
        loading={state.loading}
        // eslint-disable-next-line @typescript-eslint/dot-notation
        rowKey={state.listData?.annotations.datas[0]['dataIndex']}
        scroll={{ x: annoX, y: 258, scrollToFirstRowOnChange: true }}
        bordered={props.bordered}
        pagination={false}
        size="small"
        showSorterTooltip={false}
      />
    ) : null;
  }, [state.listData, state.loading, props.bordered, state.reptInfo]);
  const toolBar = useMemo(() => {
    if (_.isEmpty(state.reptInfo)) {
      return null;
    }
    if (props.hideParamBar) {
      return null;
    }
    const handleGroupChange = (key: string) => {
      const gid = Number(key);
      dispatch({ groupByIndex: gid, filters: [], curPage: 1 });
    };
    const butts: JSX.Element[] = [];
    if (!_.isEmpty(state.reptInfo?.groups)) {
      const gMenus: JSX.Element[] = [];
      state.reptInfo?.groups.forEach((g, i) => {
        if (i + 1 >= props.groupByIndex!) {
          const key = (i + 1) as React.Key;
          gMenus.push(<Menu.Item key={key}>{g.title}</Menu.Item>);
        }
      });
      if (gMenus.length > 1) {
        const menu = <Menu onClick={(e) => handleGroupChange(e.key)}>{gMenus}</Menu>;
        butts.push(
          <Dropdown key="select" overlay={menu}>
            <Button>
              {`层级：${state.reptInfo?.groups[state.groupByIndex! - 1].title}`}
              <DownOutlined />
            </Button>
          </Dropdown>,
        );
      }
    }
    if (!_.isEmpty(state.detailMenuItems)) {
      const menu = (
        <Menu onClick={(e) => handleDrillDownClick(e.key)}>{state.detailMenuItems}</Menu>
      );
      butts.push(
        <Dropdown key="drilldown" overlay={menu}>
          <Button>
            查看明细
            <DownOutlined />
          </Button>
        </Dropdown>,
      );
    }
    let gridSum = 0;
    const pControls: API.Control[] = [];
    const fControls: API.Control[] = [];
    state.paramControls!.forEach((c) => {
      if (c.grid! > 0) {
        pControls.push({ ...c });
        gridSum += c.grid!;
      }
      fControls.push({ ...c, grid: 24 });
    });
    let filterDrawer = null;
    for (let i = 0; i < pControls.length; i++) {
      pControls[i].grid = Math.floor((pControls[i].grid! / gridSum) * 24);
    }
    if (fControls.length > pControls.length) {
      filterDrawer = state.visibleFilterDrawer ? (
        <DataEdit
          title="筛选"
          showType="drawer"
          controls={fControls}
          initVals={state.paramVals}
          width={350}
          visible={state.visibleFilterDrawer}
          onVisibleChange={(visible) => dispatch({ visibleFilterDrawer: visible })}
          onSumit={async (vals) => {
            dispatch({ paramVals: vals as Record<string, any> });
          }}
          renderButtons={(p) => {
            return [
              <Space key="extraButtons">
                <Button type="primary" key="createRep" onClick={() => p.form?.submit?.()}>
                  确定
                </Button>
                <Button
                  type="ghost"
                  key="cancel"
                  onClick={() => dispatch({ visibleFilterDrawer: false })}
                >
                  取消
                </Button>
              </Space>,
            ];
          }}
        />
      ) : null;
      butts.push(
        <Button key="dataFilter" onClick={() => dispatch({ visibleFilterDrawer: true })}>
          数据筛选
        </Button>,
      );
    }
    return (
      <div style={{ marginBottom: 10, marginTop: -5 }}>
        {!state.reptInfo?.hideParamBar && state.paramControls!.length > 0 ? (
          <DataEdit
            controls={pControls}
            initVals={state.paramVals}
            showType="inner"
            layout="inline"
            grid={false}
            autoFocusFirstInput={false}
            onSumit={async (vals) => {
              dispatch({ paramVals: vals as Record<string, any>, curPage: 1 });
            }}
            renderButtons={(p) => {
              return [
                <Space key="extraButtons">
                  <Button type="primary" key="createRep" onClick={() => p.form?.submit?.()}>
                    查询
                  </Button>
                  {butts}
                  {props.extraButtons}
                  <span style={{ marginLeft: 50 }}>
                    {state.reptInfo!.unitTitle}
                    <Divider type="vertical" />
                    {state.reptInfo!.remarks}
                  </span>
                </Space>,
              ];
            }}
          />
        ) : null}
        {filterDrawer}
      </div>
    );
  }, [
    state.groupByIndex,
    props.hideParamBar,
    state.paramControls,
    state.paramVals,
    props.extraButtons,
    props.groupByIndex,
    state.reptInfo,
    state.visibleFilterDrawer,
    state.detailMenuItems,
    handleDrillDownClick,
  ]);
  const tableList = useMemo(() => {
    if (_.isEmpty(state.reptInfo) || _.isEmpty(state.listData)) {
      return null;
    }
    if (state.reptInfo?.type === 'chart') {
      return null;
    }
    const pageChangeHandler = (page: number) => {
      dispatch({ curPage: page, selKeys: [] });
    };
    const pageSizeChangeHandler = (pz: string) => {
      dispatch({ pageSize: Number(pz) });
    };
    const selectChangeHandler = (selRowKeys: React.Key[], selRows: object[]) => {
      dispatch({ selKeys: selRowKeys });
      if (props.onSelectChange) {
        props.onSelectChange(selRowKeys, selRows);
      }
    };
    const exportData = () => {
      if (_.isEmpty(state.reptInfo?.params)) {
        return;
      }
      dispatch({
        loading: true,
      });
      const paramVals = { ...state.paramVals };
      state.reptInfo?.params.forEach((p) => {
        if (p.dataType === 'number') {
          paramVals[p.name] = '' + paramVals[p.name];
        } else if (p.dataType === 'date') {
          paramVals[p.name] = format(paramVals[p.name], 'yyyy-MM-dd');
        }
      });
      const repReq: API.ReportRequest = {
        permID: curPermID,
        configName: props.reportName,
        reportParams: paramVals,
        groupByIndex: state.groupByIndex!,
        calcs: state.calcColVals!,
        unit: 0,
        whereAttach: props.whereAttach!,
        whereCauses: [...state.filters!, ...props.seeks!],
        orderBY: state.orderBy,
        offset: (state.curPage! - 1) * Number(state.pageSize),
        limit: state.pageSize!,
        colsDisplay: state.colsDisplay!,
      };
      ExportReportDatas(repReq)
        .then(function (resp) {
          const a = document.createElement('a');
          const url = window.URL.createObjectURL(resp.data);
          const filename = resp.response.headers.get('Content-Disposition');
          a.href = url;
          a.download = Base64.decode(filename!);
          document.body.appendChild(a);
          a.click();
          setTimeout(function () {
            document.body.removeChild(a);
          }, 1000);
          window.URL.revokeObjectURL(url);
        })
        .catch(function (ex) {
          console.error('导出报表', props.reportName, '数据错误', ex);
        })
        .finally(function () {
          dispatch({
            loading: false,
          });
        });
    };
    const clearFilter = () => {
      dispatch({ filters: [], curPage: 1 });
    };
    const getKeyVal = (record: Record<string, any>) => {
      let val = '';
      state.reptInfo?.keyColumns?.forEach((col) => (val = val + record[col] + '|'));
      return val;
    };
    const rowClickHandler = (record: Record<string, any>, rInd?: number) => {
      return {
        onClick: (event: any) => {
          event.stopPropagation();
          const val = getKeyVal(record);
          if (props.multiSelect) {
            const selKeys = [...state.selKeys!];
            const index = selKeys!.indexOf(val);
            if (index === -1) {
              selKeys!.push(val);
            } else {
              selKeys!.splice(index, 1);
            }
            const selRows: Record<string, any>[] = [];
            state.listData?.datas.forEach((item) => {
              const v = getKeyVal(item);
              if (selKeys!.includes(v)) {
                selRows.push(item);
              }
            });
            selectChangeHandler(selKeys, selRows);
          } else {
            selectChangeHandler([val], [record]);
          }
          if (!state.canSelectCell) {
            const drillDowns = state.reptInfo?.drillDowns;
            if (!drillDowns) {
              return;
            }
            const menuItems: JSX.Element[] = [];
            drillDowns.map((item, i) => {
              menuItems.push(<Menu.Item key={i as React.Key}>{item.title}</Menu.Item>);
            });
            dispatch({ detailMenuItems: menuItems, rowIndex: rInd });
          }
        },
      };
    };
    let x: number = props.width!;
    let y: number = props.height!;
    if (x === 0) {
      x = state.width!;
    }
    if (y === 0) {
      y = bodyHeight - 230 - (state.columnDeep! - 1) * 40;
    }
    if (!_.isEmpty(state.listData?.annotations) && state.listData!.annotations.total > 0) {
      y = y - 300;
    }
    const menu2 = (
      <Menu onClick={(event) => pageSizeChangeHandler(event.key)}>
        <Menu.Item key="10">每页显示10行</Menu.Item>
        <Menu.Item key="20">每页显示20行</Menu.Item>
        <Menu.Item key="30">每页显示30行</Menu.Item>
        <Menu.Item key="40">每页显示40行</Menu.Item>
        <Menu.Item key="50">每页显示50行</Menu.Item>
      </Menu>
    );
    return (
      <>
        <Table
          columns={state.columns!}
          dataSource={state.listData?.datas}
          loading={state.loading}
          onRow={rowClickHandler}
          rowKey={(record) => getKeyVal(record)}
          scroll={{ x, y }}
          bordered={props.bordered}
          rowSelection={
            state.canSelectCell
              ? undefined
              : {
                  selectedRowKeys: state.selKeys,
                  onChange: selectChangeHandler,
                  type: props.multiSelect ? 'checkbox' : 'radio',
                }
          }
          pagination={false}
          size="small"
          onChange={(_t, _f, s) => handleSorterChange(s as SorterResult<object>)}
        />
        {props.canPageSize ? (
          <Dropdown overlay={menu2} trigger={['click']}>
            <Button type="ghost" style={{ margin: 5 }}>
              每页显示{state.pageSize}行 <DownOutlined />
            </Button>
          </Dropdown>
        ) : null}
        {props.canSelectItems ? (
          <Button
            type="ghost"
            style={{ margin: 5 }}
            onClick={() => dispatch({ visibleItemDrawer: true })}
          >
            显示项目 <DownOutlined />
          </Button>
        ) : null}
        {props.canExport ? (
          <Button
            style={{ margin: 5 }}
            key="b_exportData"
            loading={state.loading}
            onClick={exportData}
          >
            导出excel
          </Button>
        ) : null}
        {props.canClearFilter ? (
          <Button style={{ margin: 5 }} key="b_clearFilter" onClick={clearFilter}>
            清除过滤条件
          </Button>
        ) : null}
        <Pagination
          style={{ margin: '5px 5px', float: 'right' }}
          className="ant-table-pagination"
          total={state.listData?.total}
          current={state.curPage}
          pageSize={state.pageSize}
          onChange={pageChangeHandler}
          showTotal={(total) => (total ? `共 ${total} 行` : '')}
          showSizeChanger={false}
        />
      </>
    );
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [
    bodyHeight,
    props.bordered,
    props.canClearFilter,
    props.canExport,
    props.canPageSize,
    props.canSelectItems,
    props.height,
    props.multiSelect,
    props.width,
    state.canSelectCell,
    state.columnDeep,
    state.columns,
    state.curPage,
    state.listData,
    state.loading,
    state.pageSize,
    state.selKeys,
    handleSorterChange,
    state.width,
    curPermID,
    state.calcColVals,
    state.colsDisplay,
    state.filters,
    state.groupByIndex,
    state.orderBy,
    state.paramVals,
    state.reptInfo?.drillDowns,
    state.reptInfo?.keyColumns,
  ]);
  const chartComp = useMemo(() => {
    if (_.isEmpty(state.reptInfo?.chart) || _.isEmpty(state.listData)) {
      return null;
    }
    if (state.reptInfo?.type !== 'chart' && state.reptInfo?.type !== 'chartList') {
      return null;
    }
    let chartDatas: Record<string, any>[] = [];
    const chart = state.reptInfo?.chart;
    const xcol = state.reptInfo!.columns.find((c) => c.key === chart!.xCol);
    const ycol = state.reptInfo!.columns.find((c) => c.key === chart!.yCol);
    let zcol: API.ReportColumn | undefined = undefined;
    if (chart!.zCol) {
      zcol = state.reptInfo!.columns.find((c) => c.key === chart!.zCol);
    }
    state.listData!.datas.map((d) => {
      let yval = d[ycol!.dataIndex];
      if (ycol!.dataType === 'float' || ycol!.dataType == 'int') {
        yval = Number(yval);
      }
      const cdata = { [xcol!.key]: d[xcol!.dataIndex], [ycol!.key]: yval };
      if (zcol) {
        cdata[zcol!.key] = Number(d[zcol!.dataIndex]);
      }
      chartDatas.push(cdata);
    });
    if (chart!.type === 'pie') {
      let sum = 0;
      chartDatas.forEach((d) => {
        sum = sum + d[ycol!.dataIndex];
      });
      chartDatas = chartDatas.map((d) => {
        return {
          ...d,
          val: d[ycol!.dataIndex],
          [ycol!.dataIndex]: Math.round((d[ycol!.dataIndex] / sum) * 100),
        };
      });
    }
    if (!chart || !chart.type || !chartDatas) {
      return null;
    }
    let Chart = null;
    if (chart.type === 'line') {
      Chart = LineChart;
    } else if (chart.type === 'column') {
      Chart = ColumnChart;
    } else if (chart.type === 'pie') {
      Chart = PieChart;
      chart.label = '日均';
      if (!_.isEmpty(state.paramVals?.colName)) {
        const colName = String(state.paramVals!.colName);
        if (colName.startsWith('ZS_') || colName.startsWith('SALE_')) {
          chart.label = '金额';
        }
      }
    } else if (chart.type === 'point') {
      Chart = PointChart;
    } else if (chart.type === 'lineColumn') {
      Chart = LineColumnChart;
    } else {
      return null;
    }
    return <Chart datas={chartDatas} {...chart} />;
  }, [state.listData, state.reptInfo, state.paramVals]);
  if (_.isEmpty(state.reptInfo) || _.isEmpty(state.listData)) {
    return (
      <div>
        <Spin tip="生成中...">
          <Alert
            message="报表生成中"
            description="此报表的数据量比较大，需要不超过30秒的生成时间，请稍后"
            type="info"
          />
        </Spin>
      </div>
    );
  }
  return (
    <div>
      {toolBar}
      {chartComp}
      {tableList}
      {selTreeDrawer}
      {annoTable}
      {state.drillDownIndex !== -1 && state.visibleSubRept ? (
        <SubReport
          drillDown={state.reptInfo!.drillDowns[state.drillDownIndex!]}
          columns={state.reptInfo!.columns}
          colName={state.curColName!}
          whereAttach={state.subWhereAtt!}
          titleColumns={state.reptInfo!.titleColumns}
          params={state.paramVals!}
          rowData={state.listData!.datas[state.rowIndex!]}
          visible={state.visibleSubRept!}
          hideDrillDownsDrawer={() => dispatch({ visibleSubRept: false })}
          hasEmid={true}
        />
      ) : null}
    </div>
  );
};
export default ReportTable;
ReportTable.defaultProps = {
  bordered: true,
  size: 'middle',
  canPage: true,
  canPageSize: true,
  canSelectItems: true,
  canClearFilter: true,
  canExport: true,
  pageSize: 20,
  width: 0,
  height: 0,
  seeks: [],
  whereAttach: '',
  paramVals: {},
  hideParamBar: false,
};
